﻿using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;

using System.IO.Ports;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;

namespace GadgeteerEnOcean
{
    //public class MainWindow : Window
    public partial class Program
    {
        int debug = 0; // debug level

        const uint sID = 0x0028F2B8; // Switch node ID
        const uint tID = 0x0400200F; // Temp-Humid Sensor node ID
        const uint cID = 0x04100169; // CO2 Sensor node ID

        bool tempHumid = true;     // Tempareture-Humid sensor available
        bool displayHumid = true;  // Display Humidity
        bool co2Sensor = false;    // CO2 Sensor available
        bool readVersion = false;  // for debug
        bool writeFilter = false;  // Filter control: If true, EEP is not received.
        bool readFilter = false;   // for debug filters

        enum PacketType
        {
            Radio = 0x01,
            Response = 0x02,
            RadioSubTel = 0x03,
            Event = 0x04,
            CommonCommand = 0x05,
            SmartAckCommand = 0x06,
            RmoteManCommand = 0x07,
            RadioMessage = 0x09,
            RadioAdvanced = 0x0A
        };

        public struct EEPData
        {
            public int porg;
            public int func;
            public int type;
            public int manID;
        }

        object RightRectangleOn(object o)
        {
            //leftRectangle.Fill = brushGreen;
            //leftRectangle.Stroke = penBlack;
            sg.DisplayRectangle(Colors.Red, thickness, Colors.Red, width + offset, y, width - offset, height);
            multicolorLed2.TurnRed();
            return null;
        }

        object RightRectangleOff(object o)
        {
            //leftRectangle.Fill = brushBlack;
            //leftRectangle.Stroke = penGreen;
            sg.DisplayRectangle(Colors.Red, thickness, Colors.Black, width + offset, y, width - offset, height);
            multicolorLed2.TurnOff();
            return null;
        }

        object LeftRectangleOn(object o)
        {
            //rightRectangle.Fill = brushRed;
            //rightRectangle.Stroke = penBlack;
            sg.DisplayRectangle(Colors.Green, thickness, Colors.Green, 0, y, width - offset, height);
            multicolorLed.TurnGreen();
            return null;
        }

        object LeftRectangleOff(object o)
        {
            //rightRectangle.Fill = brushBlack;
            //rightRectangle.Stroke = penRed;
            sg.DisplayRectangle(Colors.Green, thickness, Colors.Black, 0, y, width - offset, height);
            multicolorLed.TurnOff();
            return null;
        }

        static readonly string[] hexes = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", };
        public static string X2String(byte b)
        {
            string s = hexes[(b >> 4)];
            s += hexes[b & 0x0F];
            return s;
        }

        public static string X3String(int i)
        {
            string s = hexes[((i & 0xF00) >> 8)];
            s += hexes[((i&0xF0) >> 4)];
            s += hexes[i & 0x0F];
            return s;
        }

        public static string XnString(ref byte[] b, int length)
        {
            string s = "";
            for (int i = 0; i < length; i++)
            {
                s += X2String(b[i]);
            }
            return s;
        }

        void DisplayEEP(int func, int type, int manID)
        {
            string eepText = X2String(0xA5) + "-" + X2String((byte)func) + "-"
            + X2String((byte)type) + " " + X3String(manID);

            BeginInvoke(
                    new DispatcherOperationCallback(labelEEPText), eepText);
        }

        void BufferFilter(ref byte[] buffer, uint id)
        {
            buffer[0] = 0x55; // Sync Byte
            buffer[1] = 0; // Data Length[0]
            buffer[2] = 7; // Data Length[1]
            buffer[3] = 0; // Optional Length
            buffer[4] = 5; // Packet Type = CO (5)
            buffer[5] = crc.crc8(buffer, 1, 4); // CRC8H
            buffer[6] = 11; // Command Code = CO_WR_FILTER_ADD (11)
            buffer[7] = 0;  // FilterType = Device ID (0)
            buffer[8] = (byte)((id >> 24) & 0xFF); // ID[0]
            buffer[9] = (byte)((id >> 16) & 0xFF); // ID[1]
            buffer[10] = (byte)((id >> 8) & 0xFF); // ID[2]
            buffer[11] = (byte) (id       & 0xFF); // ID[3]
            buffer[12] = 0x80; // Filter Kind = apply (0x80)
            buffer[13] = crc.crc8(buffer, 6, 7); // CRC8D
        }

        void SerialPortThread()
        {
            uint options;
            uint inputValue;
            uint inputValue2;
            int dataLength = 0;
            int dataOffset = 0;
            byte optionalLength = 0;
            PacketType packetType = PacketType.CommonCommand;
            byte crc8h;
            byte crc8d;
            byte[] header = new byte[4];
            bool gotHeader = false;
            int rorg = 0;
            int[] data = new int[4];
            int nu = 0;
            byte[] readBuffer = new byte[40];
            byte[] writeBuffer = new byte[16];
            byte[] id = new byte[4];
            uint iID;

            if (debug > 0)
                Debug.Print("Serial port started");

            if (readVersion)
            {
                Debug.Print("Read Version");
                writeBuffer[0] = 0x55; // Sync Byte
                writeBuffer[1] = 0; // Data Length[0]
                writeBuffer[2] = 1; // Data Length[1]
                writeBuffer[3] = 0; // Optional Length
                writeBuffer[4] = 5; // Packet Type = CO (5)
                writeBuffer[5] = crc.crc8(writeBuffer, 1, 4); // CRC8H
                writeBuffer[6] = 3; // Command Code = CO_RD_VERSION (3)
                writeBuffer[7] = crc.crc8(writeBuffer, 6, 1); // CRC8D

                serialPort.Write(writeBuffer, 0, 8);
                serialPort.Flush();
                Thread.Sleep(100);
            }
            if (writeFilter)
            {
                if (true)
                {
                    Debug.Print("Clear all Filters");
                    writeBuffer[0] = 0x55; // Sync Byte
                    writeBuffer[1] = 0; // Data Length[0]
                    writeBuffer[2] = 1; // Data Length[1]
                    writeBuffer[3] = 0; // Optional Length
                    writeBuffer[4] = 5; // Packet Type = CO (5)
                    writeBuffer[5] = crc.crc8(writeBuffer, 1, 4); // CRC8H
                    writeBuffer[6] = 13; // Command Code = CO_WR_FILTER_DEL (13)
                    writeBuffer[7] = crc.crc8(writeBuffer, 6, 1); // CRC8D
                    serialPort.Write(writeBuffer, 0, 8);
                    serialPort.Flush();
                    Thread.Sleep(100);
                }
                if (true)
                {
                    Debug.Print("Switch Filter Add");
                    BufferFilter(ref writeBuffer, sID);
                    serialPort.Write(writeBuffer, 0, 14);
                    serialPort.Flush();
                    Thread.Sleep(100);
                }
                if (tempHumid)
                {
                    Debug.Print("Temp-Humid sensor Add");
                    BufferFilter(ref writeBuffer, tID);
                    serialPort.Write(writeBuffer, 0, 14);
                    serialPort.Flush();
                    Thread.Sleep(100);
                }
                if (co2Sensor)
                {
                    Debug.Print("CO2 sensor Add");
                    BufferFilter(ref writeBuffer, cID);
                    serialPort.Write(writeBuffer, 0, 14);
                    serialPort.Flush();
                    Thread.Sleep(100);
                }
            }
            if (writeFilter)
            {
                Debug.Print("Enable Filters");
                writeBuffer[0] = 0x55; // Sync Byte
                writeBuffer[1] = 0; // Data Length[0]
                writeBuffer[2] = 3; // Data Length[1]
                writeBuffer[3] = 0; // Optional Length
                writeBuffer[4] = 5; // Packet Type = CO (5)
                writeBuffer[5] = crc.crc8(writeBuffer, 1, 4); // CRC8H
                writeBuffer[6] = 14; // Command Code = CO_WR_FILTER_ENABLE (14)
                writeBuffer[7] = 1;  // Filter Enable = ON (1)
                writeBuffer[8] = 0;  // Filter Operator = OR (0)
                //writeBuffer[8] = 1;  // Filter Operator = AND (1)
                writeBuffer[9] = crc.crc8(writeBuffer, 6, 3); // CRC8D

                serialPort.Write(writeBuffer, 0, 10);
                serialPort.Flush();
                Thread.Sleep(100);
            }
            if (readFilter)
            {
                Debug.Print("Check Registered Filters");
                writeBuffer[0] = 0x55; // Sync Byte
                writeBuffer[1] = 0; // Data Length[0]
                writeBuffer[2] = 1; // Data Length[1]
                writeBuffer[3] = 0; // Optional Length
                writeBuffer[4] = 5; // Packet Type = CO (5)
                writeBuffer[5] = crc.crc8(writeBuffer, 1, 4); // CRC8H
                writeBuffer[6] = 15; // Command Code = CO_RD_FILTER (15)
                writeBuffer[7] = crc.crc8(writeBuffer, 6, 1); // CRC8D

                serialPort.Write(writeBuffer, 0, 8);
                serialPort.Flush();
                Thread.Sleep(100);
            }

            while (true)
            {
                do
                {
                    try
                    {
                        do
                        {
                            serialPort.Read(readBuffer, 0, 1);
                        }
                        while (readBuffer[0] != 0x55);

                        serialPort.Read(header, 0, 4);

                        dataLength = header[0] << 8 | header[1];
                        optionalLength = header[2];
                        packetType = (PacketType)header[3];

                        serialPort.Read(readBuffer, 0, 1);
                        crc8h = readBuffer[0];

                        gotHeader = crc8h == crc.crc8(header);
                    }
                    catch (Exception e)
                    {
                        Debug.Print(e.Message);
                        return;
                    }
                }
                while (!gotHeader);

                if (debug > 1)
                    Debug.Print("Got header");

                if (dataLength > 40)
                    dataLength = 40;

                for (int i = 0; i < readBuffer.Length; i++)
                {
                    // clear buffer for debug
                    readBuffer[i] = 0;
                }
                for (int i = 0; i < dataLength; i++)
                {
                    serialPort.Read(readBuffer, i, 1);
                }

                if (packetType == PacketType.Radio || packetType == PacketType.RadioAdvanced)
                {
                    rorg = readBuffer[0];
                    dataOffset = rorg == 0x62 ? 2 : 0;

                    id[3] = readBuffer[1 + dataOffset];
                    id[2] = readBuffer[2 + dataOffset];
                    id[1] = readBuffer[3 + dataOffset];
                    id[0] = readBuffer[4 + dataOffset];

                    if (rorg == 0x20) // RPS
                    {
                        //dataSize = 1;
                        nu = (readBuffer[5 + dataOffset] >> 7) & 0x01;
                        data[0] = readBuffer[5 + dataOffset] & 0x0F;
                    }
                    else if (rorg == 0x22) // 4BS
                    {
                        //dataSize = 4;
                        data[0] = readBuffer[5 + dataOffset];
                        data[1] = readBuffer[6 + dataOffset];
                        data[2] = readBuffer[7 + dataOffset];
                        data[3] = readBuffer[8 + dataOffset];
                    }
                    else if (rorg == 0x62)  // Teach-In
                    {
                        //dataSize = 4;
                        data[0] = readBuffer[5 + dataOffset];
                        data[1] = readBuffer[6 + dataOffset];
                        data[2] = readBuffer[7 + dataOffset];
                        data[3] = readBuffer[8 + dataOffset];
                    }
                    else
                    {
                        Debug.Print("Unknown rorg = " + rorg);
                    }
                }
                else if (packetType == PacketType.Response)
                {
                    Debug.Print("dataLength:" + dataLength + " RES:" + readBuffer[0]);

                    data[0] = readBuffer[0]; // Return Code
                    data[1] = readBuffer[1]; //APP Version[0]
                    data[2] = readBuffer[2]; //APP Version[1]
                    data[3] = readBuffer[3]; //APP Version[2]

                    continue;
                }
                else
                {
                    // We ignore other type.
                    continue;
                }

                if (optionalLength > 0)
                {
                    serialPort.Read(readBuffer, dataLength, optionalLength);
                }

                serialPort.Read(readBuffer, dataLength + optionalLength, 1);
                crc8d = readBuffer[dataLength + optionalLength];

                if (crc8d != crc.crc8(readBuffer, dataLength + optionalLength))
                {
                    return;
                }

                // Display Data
                if (debug > 1)
                {
                    string IdData = "Data: ";
                    for (int i = 0; i < 4; i++)
                    {
                        IdData += data[i].ToString("X2");
                    }
                    Debug.Print(IdData);
                }
                //more,...
                //iID = BitConverterToInt32(id, 0);
                iID = (uint)((id[0])
                        | (id[1] << 8)
                        | (id[2] << 16)
                        | (id[3] << 24));

                if (iID != 0)
                {
                    options = (uint)data[3]; // Read Input Value from data[3] //
                    inputValue = (uint)data[2]; // Read Input Value from data[2] //
                    inputValue2 = (uint)data[1]; // Read Input Value from data[1] //

                    if (debug > 1)
                    {
                        string s = iID.ToString("X8");

                        string IdText = "ID: ";
                        IdText += iID.ToString("X8");

                        IdText += " " + inputValue.ToString("X2")
                            + " " + inputValue2.ToString("X2") + "\r\n";

                        Debug.Print(IdText);
                    }

                    //
                    // Check and Dispatch by ID and rorg
                    //
                    if (rorg == 0x62) // Teach-In
                    {
                        // get EEP, Teach In Telegram
                        uint func = ((uint)data[0]) >> 2;
                        uint type = ((uint)data[0] & 0x03) << 5 | ((uint)data[1]) >> 3;
                        int manID = (data[1] & 0x07) << 8 | data[2];

                        if (debug > 2)
                            Debug.Print("Teach In: A5-" +
                                func.ToString("X2") + "-" +
                                type.ToString("X2") + " " +
                                manID.ToString("X3"));
                        if (manID != 0)
                            DisplayEEP((int)func, (int)type, manID);
                    }
                    else if (rorg == 0x22 && iID == tID)
                    {
                        if (debug > 2)
                            Debug.Print("4BS Temp-Humid");

                        if (displayHumid)
                        {
                            //DisplayHumidity(inputValue2);
                            double value = (inputValue2 - 5) * 100 / 250;
                            if (value < 0.0)
                                value = 0.0;
                            string humid = value.ToString("F1") + " %";

                            BeginInvoke(
                                new DispatcherOperationCallback(DisplayHumidity),
                                humid);

                            //DisplayTemperature(inputValue);
                            value = ((inputValue - 5) * 40 / 250) - 5;
                            if (value < 0.0)
                                value = 0.0;
                            string temp = value.ToString("F1") + "\u00B0C";

                            BeginInvoke(
                                new DispatcherOperationCallback(DisplayTemperature),
                                temp);
                        }
                        else
                        {
                            //DisplayTemperature(inputValue);
                            double value = ((-40 * inputValue) / 255) + 40;
                            if (value < 0.0)
                                value = 0.0;
                            string temp = value.ToString("F1") + "\u00B0C";

                            BeginInvoke(
                                new DispatcherOperationCallback(DisplayTemperature),
                                temp);
                        }
                    }
                    else if (rorg == 0x22 && iID == cID)
                    {
                        if (debug > 2)
                            Debug.Print("4BS CO2");

                        if (co2Sensor)
                        {
                            //DisplayCO2(inputValue2);
                            int value = (int) (inputValue2) * 10;
                            string co2 = value.ToString() + " ppm";

                            BeginInvoke(
                                new DispatcherOperationCallback(DisplayCO2),
                                co2);
                        }
                    }
                    else if (rorg == 0x20) // RPS
                    {
                        if (debug > 2)
                            Debug.Print("RPS Telegram, nu=" + nu);

                        if (nu == 0x01)
                        {
                            uint mask = 0x01;
                            for (int i = 0; i < 4; i++)
                            {
                                switch (data[0] & mask)
                                {
                                    case 0x01:
                                        BeginInvoke(
                                            new DispatcherOperationCallback(
                                                    LeftRectangleOn), 0);
                                        break;
                                    case 0x02:
                                        BeginInvoke(
                                            new DispatcherOperationCallback(
                                                    LeftRectangleOff), 0);
                                        break;
                                    case 0x04:
                                        BeginInvoke(
                                            new DispatcherOperationCallback(
                                                    RightRectangleOn), 0);
                                        break;
                                    case 0x08:
                                        BeginInvoke(
                                            new DispatcherOperationCallback(
                                                    RightRectangleOff), 0);
                                        break;
                                } //switch
                                mask <<= 1;
                            } // for
                        } // if
                    } //else if
                }
                else
                {
                    if (debug > 2)
                        Debug.Print("Zero ID");
                }
            }
        }
    }

    public static class crc
    {
        private static Byte[] crc8Table = new Byte[]
        {
            0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15,
            0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
            0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65,
            0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d,
            0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5,
            0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
            0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85,
            0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd,
            0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2,
            0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea,
            0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2,
            0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a,
            0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32,
            0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a,
            0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
            0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a,
            0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c,
            0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4,
            0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
            0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4,
            0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c,
            0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44,
            0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c,
            0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
            0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b,
            0x76, 0x71, 0x78, 0x7f, 0x6A, 0x6d, 0x64, 0x63,
            0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b,
            0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13,
            0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb,
            0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8D, 0x84, 0x83,
            0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb,
            0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3
        };

        public static Byte crc8(Byte[] data, int offset, int count)
        {
            Byte crc = 0;
            count += offset;
            for (int i = offset; i < count; i++)
                crc = crc8Table[crc ^ data[i]];
            return crc;
        }

        public static Byte crc8(Byte[] data, int count)
        {
            return crc8(data, 0, count);
        }

        public static Byte crc8(Byte[] data)
        {
            return crc8(data, data.Length);
        }
    }
}
